/*
 * Copyright (C) 2011 Dirk Weißenborn, dirk.weissenborn@gmail.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.weissenborn.colabtex.server;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Restrictions;

import com.weissenborn.colabtex.client.service.EtherpadService;
import com.weissenborn.colabtex.server.database.HibernateUtil;
import com.weissenborn.colabtex.server.etherpad.EtherpadUtils;
import com.weissenborn.colabtex.server.tex.TexUtil;
import com.weissenborn.colabtex.server.user.ColabTexUser;
import com.weissenborn.colabtex.server.user.IdNameMapping;
import com.weissenborn.colabtex.server.user.Permission;
import com.weissenborn.colabtex.server.user.Permission.Role;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * The Class EtherpadServiceImpl handles async requests from the client.
 */
public class EtherpadServiceImpl extends RemoteServiceServlet implements EtherpadService {
	
	/** The etherpad server instance. */
	EtherpadUtils etherpad = new EtherpadUtils("com/weissenborn/colabtex/etherpad.ini");
	
	/* (non-Javadoc)
	 * @see com.weissenborn.colabtex.client.service.EtherpadService#createGroup(java.lang.String, java.util.Map)
	 */
	public Boolean createGroup(String groupName, Map<String, String> members) {
		//prevent sql injection
		if(groupName.contains(" "))
				return false;
		Session session = HibernateUtil.getSessionFactory().openSession();
		
		IdNameMapping existingGroup = (IdNameMapping) session.createCriteria(IdNameMapping.class).
										add(Restrictions.eq("groupName", groupName)).uniqueResult();
		if(existingGroup!= null) {
			session.close();
			return false;
		}
		
		String groupId = etherpad.createGroup();
		if(groupId == null)
			return false;
		
		Transaction t = session.beginTransaction();
		
		try {			
			IdNameMapping map = new IdNameMapping(groupId, groupName);
			session.saveOrUpdate(map);		
			
			for (Entry<String, String> member : members.entrySet()) {		
				ColabTexUser user = (ColabTexUser) session.createCriteria(ColabTexUser.class).
							           add(Restrictions.eq("name", member.getKey())).uniqueResult();
				
				if(user == null) {
					continue;
				}
				
				Role role = Role.editor;
				if(member.getValue().equalsIgnoreCase("Admin"))
					role = Role.admin;
				else if(member.getValue().equalsIgnoreCase("Editor"))
					role = Role.editor;
				else if(member.getValue().equalsIgnoreCase("Read-only"))
					role = Role.reader;
				if(user.getPermissions()==null)
					user.setPermissions(new ArrayList<Permission>());
				
				Permission permission = new Permission(groupName, role);			
				session.saveOrUpdate(permission);
				user.getPermissions().add(permission);
				
				session.update(user);			
			}
	
			t.commit();
			session.close();		
			return true;
		}
		catch (Exception e) {
			etherpad.deleteGroup(groupId);
			t.rollback();
			session.close();
			e.printStackTrace();
			return false;
		}
	}

	/* (non-Javadoc)
	 * @see com.weissenborn.colabtex.client.service.EtherpadService#createGroupPad(java.lang.String, java.lang.String)
	 */
	public Boolean createGroupPad(String groupName, String padName) {
		//prevent sql injection
		if(padName.contains(" "))
				return false;
		
		String id = getGroupId(groupName);
		
		return etherpad.createGroupPad(id, padName);
	}

	/**
	 * Gets the group id.
	 *
	 * @param name the name
	 * @return the group id
	 */
	private String getGroupId(String name) {
		//prevent sql injection
		if(name.contains(" "))
				return null;
		
		Session session = HibernateUtil.getSessionFactory().openSession();
		
		IdNameMapping map = (IdNameMapping)session.createCriteria(IdNameMapping.class).add(Restrictions.eq("groupName", name)).uniqueResult();
		session.close();
		
		return map.getGroupId();
	}

	/* (non-Javadoc)
	 * @see com.weissenborn.colabtex.client.service.EtherpadService#deleteGroupPad(java.lang.String, java.lang.String)
	 */
	public void deleteGroupPad(String groupName, String padName) {
		String id = getGroupId(groupName);
		etherpad.deletePad(id+"$"+padName);
	}

	/* (non-Javadoc)
	 * @see com.weissenborn.colabtex.client.service.EtherpadService#listPads(java.lang.String, java.lang.String)
	 */
	public Map<String,String> listPads(String permission, String groupName) {
		Map<String,String> result = new HashMap<String, String>();
		String id = getGroupId(groupName);
		String[] padList = etherpad.listPads(id);
		if(padList == null)
			return result;
		for (String string : padList) {
			if(permission.equalsIgnoreCase("Reader"))
				result.put(string, etherpad.getReadOnlyPadUrl(string));
			else
				result.put(string, etherpad.getPadUrl(string));
		}
		return result;
	}

	/* (non-Javadoc)
	 * @see com.weissenborn.colabtex.client.service.EtherpadService#deleteGroup(java.lang.String)
	 */
	public Boolean deleteGroup(String groupName) {
		String id = getGroupId(groupName);
		etherpad.deleteGroup(id);
		TexUtil.deleteGroupFolder(id);
		
		Session session = HibernateUtil.getSessionFactory().openSession();
		Transaction t = session.beginTransaction();
		
		Query q1 = session.createSQLQuery("SELECT NAME FROM PERMISSIONS WHERE groupname='"+groupName+"'");
		List<String> users = q1.list();
		
		for (String username : users) {
			ColabTexUser user = (ColabTexUser) session.createCriteria(ColabTexUser.class).
			           add(Restrictions.eq("name", username)).uniqueResult();
			if(user == null) {
				continue;
			}
			List<Permission> permissions = user.getPermissions();		
			
			for (int i = 0; i < permissions.size(); i++) {
				if(permissions.get(i).getGroupName().equals(groupName)) {
					permissions.remove(i);
					break;
				}
			}
		}	
		
		t.commit();
		
		t = session.beginTransaction();
		Query q2 = session.createSQLQuery("DELETE FROM PERMISSION WHERE groupname='"+groupName+"'");
		Query q3 = session.createSQLQuery("DELETE FROM IDNAMEMAPPING WHERE groupname='"+groupName+"'");
		
		q2.executeUpdate();
		q3.executeUpdate();
		t.commit();
		
		session.close();
		
		return true;
	}

	/* (non-Javadoc)
	 * @see com.weissenborn.colabtex.client.service.EtherpadService#listGroups(java.lang.String)
	 */
	public Map<String,String> listGroups(String userName) {
		Session session = HibernateUtil.getSessionFactory().openSession();
		
		ColabTexUser user = (ColabTexUser) session.createCriteria(ColabTexUser.class).
					add(Restrictions.eq("name", userName)).uniqueResult();
		if(user==null) {
			session.close();
			return new HashMap<String, String>();
		}
		
		Map<String,String> result = new HashMap<String, String>();
		
		if(user.getPermissions()!=null)
			for (Permission p : user.getPermissions()) {
				if(p==null)
					continue;
				result.put(p.getGroupName(), p.getRole().toString());
			}
		session.close();
		
		return result;
	}

	/* (non-Javadoc)
	 * @see com.weissenborn.colabtex.client.service.EtherpadService#addPermission(java.lang.String, java.lang.String, java.lang.String)
	 */
	public Boolean addPermission(String groupName, String memberName, String role) {
		//prevent sql injection
		if(memberName.contains(" "))
				return null;
				
		Session session = HibernateUtil.getSessionFactory().openSession();
		Transaction t = session.beginTransaction();
		
		ColabTexUser user = (ColabTexUser) session.createCriteria(ColabTexUser.class).
		           add(Restrictions.eq("name", memberName)).uniqueResult();
		if(user == null) {
			return false;
		}
		List<Permission> permissions = user.getPermissions();
		boolean successful = false;
		Role urole = Role.editor;
		if(role.equalsIgnoreCase("Admin"))
			urole = Role.admin;
		else if(role.equalsIgnoreCase("Editor"))
			urole = Role.editor;
		else if(role.equalsIgnoreCase("Read-only"))
			urole = Role.reader;
		
		Permission permission = new Permission(groupName, urole);
		session.saveOrUpdate(permission);
		
		for (int i = 0; i < permissions.size(); i++) {
			if(permissions.get(i).getGroupName().equals(groupName)) {
				permissions.get(i).setRole(urole);
				successful=true;
				break;
			}
		}
		
		if(!successful)
			permissions.add(permission);
		session.update(user);
		t.commit();
		session.close();
		return true;
	}

	
	/* (non-Javadoc)
	 * @see com.weissenborn.colabtex.client.service.EtherpadService#removePermission(java.lang.String, java.lang.String, java.lang.Boolean)
	 */
	public Boolean removePermission(String groupName, String memberName, Boolean yourself) {
		Session session = HibernateUtil.getSessionFactory().openSession();
		Transaction t = session.beginTransaction();
		
		ColabTexUser user = (ColabTexUser) session.createCriteria(ColabTexUser.class).
		           add(Restrictions.eq("name", memberName)).uniqueResult();
		if(user == null) {
			return false;
		}
		List<Permission> permissions = user.getPermissions();		
		
		for (int i = 0; i < permissions.size(); i++) {
			if(permissions.get(i).getGroupName().equals(groupName)) {
				if(permissions.get(i).getRole().equals(Permission.Role.admin))
					if(!yourself)
						return false;
					else {
						Query memberQuery = session.createSQLQuery("select name, role from Permissions where groupname='"+groupName+"' and role='admin'");
						List list = memberQuery.list();
						if(list.size()<2)
							return false;
					}
				permissions.remove(i);
				t.commit();
				session.close();
				return true;
			}
		}
		session.close();
		return false;
	}

	/* (non-Javadoc)
	 * @see com.weissenborn.colabtex.client.service.EtherpadService#listMembers(java.lang.String)
	 */
	public Map<String, String> listMembers(String groupName) {
		Map<String, String> result = new HashMap<String, String>();
		
		Session session = HibernateUtil.getSessionFactory().openSession();
		
		Query memberQuery = session.createSQLQuery("select name, role from Permissions where groupname='"+groupName+"'");
		List list = memberQuery.list();
		
		
		for (Iterator iterator = list.iterator(); iterator.hasNext();) {
			Object[] object = (Object[]) iterator.next();
			result.put((String)object[0], (String) object[1]);
		}
		session.close();
		
		return result;
	}
}
